
var clicked_down_callback = null;
var fixed = null;
var drawing = null;
var drawing_timeout = null;
var canvas_div;

var tile_size = 6; // power of 2
var octaves = 5; 
var color_level = 4;

var old_tiles = new Array();
var tiles = new Array();

var old_edges = new Array();
var edges = new Array();

var old_corners = new Array();
var corners = new Array();

var invoke_text = "In meteorology, a cloud is a visible mass of liquid droplets or frozen crystals made of water or various chemicals suspended in the atmosphere above the surface of a planetary body. These suspended particles are also known as aerosols. Clouds in earth's atmosphere are studied in the cloud physics branch of meteorology. Two processes, possibly acting together, can lead to air becoming saturated: cooling the air or adding water vapor to the air. In general, precipitation will fall to the surface; an exception is virga, which evaporates before reaching the surface.";

function on_resize() {
	if (null == fixed) {
		canvas.setAttribute('width', window.innerWidth * window.devicePixelRatio);
		canvas.setAttribute('height', window.innerHeight * window.devicePixelRatio);
		//var s = 1/window.devicePixelRatio;
		//ctx.scale(s, s);
		var cw = window.innerWidth / window.devicePixelRatio;
		var ch = window.innerHeight / window.devicePixelRatio;
		canvas.style.width = window.innerWidth + 'px';
		canvas.style.height = window.innerHeight + 'px';

	} else {
		canvas.width = fixed.w;
		canvas.height = fixed.h;
		document.body.style.backgroundColor = 'gray';
	}
	click_div.style.width = window.innerWidth + 'px';
	click_div.style.height = window.innerHeight + 'px';
	ctx.globalCompositeOperation = 'lighter';
}


function init() {
	//document.addEventListener("touchstart", touch_started, false);
	document.addEventListener("touchmove", touch_moved, false);
	//document.addEventListener("touchend", touch_ended, false);
	click_div = document.getElementById('click_div');
	click_div.style.backgroundColor = "rgba(0,0,0,0)";
	canvas_div = document.getElementById('canvas_div');
	status_div = document.getElementById('status_div');
	canvas_init();
	clicked_down_callback = did_click;
	click_div.addEventListener('mousedown', clicked_down, false);
	click_div.addEventListener('touchstart', touch_started, false);
	window.onresize = on_resize;
	on_resize();
	animate_screen();
}

function touch_started(e) {
	//debug_log('touch_started');
	e.preventDefault();
	if (e.touches.length < 1) {
		return;
	}
	var o = {
		clientX : e.touches[0].pageX,
		clientY : e.touches[0].pageY,
		preventDefault: (function () { })
	};
	clicked_down(o);
}

function touch_moved(e) { 
	e.preventDefault();
}

function clicked_down(e) {
	e.preventDefault();
	if (null == clicked_down_callback) { return; }
	clicked_down_callback(e);
}

function did_click(e) {
	var hx = 1 - 2 * e.clientX / window.innerWidth;
	var hy = 1 - 2 * e.clientY / window.innerHeight;
	do_click(-hx, -hy);
}

function do_click(x, y) {
	//animate_screen();
}

function find_edge(h, x, y, o, black) {
	var k = [h, x, y, o].join(":");
	if (null == edges[k]) {
		edges[k] = new Array();
		var i;
		for (i = 0; i < Math.pow(2, o) - 1; ++i) {
			edges[k].push([
				(black ? 0 : Math.random()), 
				(black ? 0 : Math.random()), 
				(black ? 0 : Math.random())
			]);
		}
	}
	return edges[k];
}

function find_corner(x, y, o, black) {
	var k = [x, y, o].join(":");
	if (null == corners[k]) {
		corners[k] = [
			(black ? 0 : Math.random()), 
			(black ? 0 : Math.random()), 
			(black ? 0 : Math.random())
		];
	}
	return corners[k];
}

function init_tiles() {
	// we want to divide the screen up into tiles of 2^n x 2^n
	var r = Math.pow(2, tile_size);
	var rows = Math.floor(canvas.height / r);
	var cols = Math.floor(canvas.width / r);
	if (rows % 2) { --rows; }
	if (cols % 2) { --cols; }

	var o_x = Math.round(canvas.width / 2);
	var o_y = Math.round(canvas.height / 2);

	var row = 0;
	var col = 0;

	var x = 0;
	var y = 0;
	var z = 0;

	var max_x = o_x;
	var max_y = o_y;
	var min_y = o_y;

	for (row = 0; row < rows / 2; ++row) {
		for (col = 0; col < cols / 2; ++col) {
			x = o_x + col * r;
			y = o_y + row * r;
			z = o_x - (col + 1) * r;
			if (x > max_x) { max_x = x; }
			if (y > max_y) { max_y = y; }
			tiles.push(new_tile(x, y, r, r, z));
			x = o_x + col * r;
			y = o_y - (row + 1) * r;
			z = o_x - (col + 1) * r;
			if (y < min_y) { min_y = y; }
			tiles.push(new_tile(x, y, r, r, z));
		}
	}

	x = max_x + r;
	for (row = 0; row < rows / 2; ++row) {
		y = o_y + row * r;
		for (z = 1; z <= octaves; ++z) {
			find_edge("v", x, y, z, true);
			find_corner(x, y, z, true);
		}
		y = o_y - (row + 1) * r;
		for (z = 1; z <= octaves; ++z) {
			find_edge("v", x, y, z, true);
			find_corner(x, y, z, true);
		}
	}

	for (z = 1; z <= octaves; ++z) {
		find_corner(x, max_y + r, z, true);
	}

	y = max_y + r;
	for (col = 0; col < cols / 2; ++col) {
		x = o_x + col * r;
		for (z = 1; z <= octaves; ++z) {
			find_edge("h", x, min_y, z, true);
			find_edge("h", x, y, z, true);
			find_corner(x, min_y, z, true);
			find_corner(x, y, z, true);
		}
	}

	
}

function new_tile(x, y, w, h, z) {
	var t = old_tiles.shift();
	if (null == t) {
		t = new Object();
	}

	t.x = x;
	t.y = y;
	t.w = w;
	t.h = h;
	t.z = z;

	return t;
}

function animate_screen() {
	// divide the screen up into tiles
	init_tiles();
	// draw each one over time
	draw_next_tiles();
}

function draw_next_tiles() {
	if (0 == tiles.length) {
		return;
	}
	var n = Math.floor(Math.random() * tiles.length);
	var tile = tiles[n];
	tiles[n] = tiles[0];
	tiles.shift();
	render_tile(tile);
	old_tiles.push(tile);
	setTimeout(draw_next_tiles, 50);
}

function render_tile(t) {
	if (t.x < 0 || t.y < 0) { return; }
	if ((t.x + t.w) >= canvas.width) { return; }
	if ((t.y + t.h) >= canvas.height) { return; }
	var i = 0;
	var g = true;
	for ( i = 1; i <= octaves; ++i) {
		g = (i >= color_level ? null : true);
		draw_fixed_clouds(t.w, t.x, t.y, i, g);
	}
	threshold(t.w, t.x, t.y, 127);
	mirror(t.w, t.x, t.y, t.z);
}

function draw_random_clouds() {
	var gr = 256;
	var gx = Math.floor((canvas.width - gr) * Math.random());
	var gy = Math.floor((canvas.height - gr) * Math.random());
	draw_fixed_clouds(gr, gx, gy, 1);
	draw_fixed_clouds(gr, gx, gy, 2);
	draw_fixed_clouds(gr, gx, gy, 3);
	draw_fixed_clouds(gr, gx, gy, 4);
	draw_fixed_clouds(gr, gx, gy, 5);
	draw_fixed_clouds(gr, gx, gy, 6);
}

function enhance(gr, gx, gy) {
	var d = ctx.getImageData(gx, gy, gr, gr);
	var col = 0;
	var row = 0;
	var i = 0;
	var c = 0;
	var v = 0;
	for (row = 0; row < gr; ++row) {
		for (col = 0; col < gr; ++col) {
			for (c = 0; c < 3; ++c) {
				v = d.data[i + c] / 255;
				v *= v;
				d.data[i + c] = Math.floor(v * 255);
			}
			d.data[i + 3] = 255;

			i += 4;
		}
	}
	var d = ctx.putImageData(d, gx, gy);
}

function mirror(gr, gx, gy, gz) {
	var d = ctx.getImageData(gx, gy, gr, gr);
	var col = 0;
	var row = 0;
	var i = 0;
	var j = 0;
	var c = 0;
	var v = 0;
	for (row = 0; row < gr; ++row) {
		j = i + gr * 4;
		for (col = 0; col < gr; ++col) {
			j -= 4;
			if (col < gr / 2) {
				for (c = 0; c < 3; ++c) {
					v = d.data[i + c];
					d.data[i + c] = d.data[j + c];
					d.data[j + c] = v;
				}
			}
			d.data[i + 3] = 255;

			i += 4;
		}
	}
	var d = ctx.putImageData(d, gz, gy);
}

function threshold(gr, gx, gy, gv) {
	var d = ctx.getImageData(gx, gy, gr, gr);
	var col = 0;
	var row = 0;
	var i = 0;
	var c = 0;
	for (row = 0; row < gr; ++row) {
		for (col = 0; col < gr; ++col) {
			for (c = 0; c < 3; ++c) {
				d.data[i + c] = (
					(d.data[i + c] > gv) ? 255 : 0
				);
			}
			d.data[i + 3] = 255;

			i += 4;
		}
	}
	var d = ctx.putImageData(d, gx, gy);
}

function draw_fixed_clouds(gr, gx, gy, octave, monochrome) {
	var o = Math.floor(octave);
	if (o < 1) { return; }
	o = Math.pow(2, o);
	o_weight = 2 / o;
	// first, we need an o x o array
	var p = new Array();
	var i, j;
	for (i = 0; i <= o; ++i) { 
		p[i] = new Array();
		for (j = 0; j <= o; ++j) {
			p[i][j] = [
				Math.random(), // red
				Math.random(), // green
				Math.random() // blue
			];

		}
	}

	//find the corners
	var corner;

	// lt
	corner = find_corner(gx, gy, octave);
	p[0][0] = corner;

	// rt
	corner = find_corner(gx + gr, gy, octave);
	p[0][o] = corner;

	// lb
	corner = find_corner(gx, gy + gr, octave);
	p[o][0] = corner;

	// rb
	corner = find_corner(gx + gr, gy + gr, octave);
	p[o][o] = corner;


	//find the edges

	var edge;

	// top
	edge = find_edge("h", gx, gy, octave);
	for (i = 0; i < edge.length; ++i) {
		p[0][i + 1] = edge[i];
	}

	// bottom
	edge = find_edge("h", gx, gy + gr, octave);
	for (i = 0; i < edge.length; ++i) {
		p[o][i + 1] = edge[i];
	}

	// left
	edge = find_edge("v", gx, gy, octave);
	for (i = 0; i < edge.length; ++i) {
		p[i + 1][0] = edge[i];
	}

	// right
	edge = find_edge("v", gx + gr, gy, octave);
	for (i = 0; i < edge.length; ++i) {
		p[i + 1][o] = edge[i];
	}


	var d = ctx.getImageData(gx, gy, gr, gr);
	var col = 0;
	var row = 0;
	var m = gr * gr * 4;
	var h = gr / 2;
	var dx;
	var dy;

	i = 0;
	var row_gradient = 0;
	var col_gradient = 0;
	var row_weight = 0;
	var col_weight = 0;
	var col_section = 0;
	for (row = 0; row < gr; ++row) {
		row_gradient = Math.floor(255 * row / gr);
		row_weight = o * row / gr;
		row_section = Math.floor(row_weight);
		row_weight -= row_section;

		p_lt = null;
		p_rt = null;
		p_lb = null;
		p_rb = null;


		var c = 0;
		for (col = 0; col < gr; ++col) {

			col_gradient = Math.floor(255 * col / gr);
			col_weight = o * col / gr;
			col_section = Math.floor(col_weight);
			col_weight -= col_section;

			p_lt = p[row_section][col_section];
			p_rt = p[row_section][col_section + 1];
			p_lb = p[row_section + 1][col_section];
			p_rb = p[row_section + 1][col_section + 1];

			for (c = 0; c < 3; ++c) {
				d.data[i + c] = Math.floor(
				255 * (
					(p_lt[c] * (1 - col_weight) +
					p_rt[c] * col_weight) * 
					(1 - row_weight) +
					(p_lb[c] * (1 - col_weight) +
					p_rb[c] * col_weight) * 
					row_weight
				) * o_weight +
				d.data[i + c] * (1 - o_weight)
				)
				if (null != monochrome) {
					d.data[i + 1] = d.data[i];
					d.data[i + 2] = d.data[i];
					break;
				}
			}
			d.data[i + 3] = 255;
			i += 4;
		}
	}

	var d = ctx.putImageData(d, gx, gy);
}

function draw_random_dot() {
	var range = 50 + Math.random() * 200;
	var gr = Math.floor(1 + range * Math.random());
	var gx = Math.floor((canvas.width - gr) * Math.random());
	var gy = Math.floor((canvas.height - gr) * Math.random());
	var cr = Math.random();
	var cg = Math.random();
	var cb = Math.random();
	var ca = Math.random();

	cr = Math.floor(cr * 255);
	cg = Math.floor(cg * 255);
	cb = Math.floor(cb * 255);
	draw_fixed_dot(gr, gx, gy, cr, cg, cb, ca);
	draw_fixed_dot(gr, canvas.width - gr - gx, gy, cr, cg, cb, ca);
}

function draw_fixed_dot(gr, gx, gy, cr, cg, cb, ca) {
	var d = ctx.getImageData(gx, gy, gr, gr);
	var i;
	var col = 0;
	var row = 0;
	var m = gr * gr * 4;
	var h = gr / 2;
	var dx;
	var dy;

	for (i = 0; i < m; i += 4) {
		++col;
		if (col >= gr) { ++row; col = 0; }

		// distance from center

		dx = h - row;
		dy = h - col;
		dx *= dx;
		dy *= dy;

		dx = Math.sqrt(dx + dy);

		if (dx > h) { continue; }

		dx = dx / h;
		dy = 1 - dx;

		dy *= ca;
		dx = 1 - dy;

		d.data[i] = Math.floor(cr * dy + dx * d.data[i]);
		d.data[i + 1] = 
			Math.floor(cg * dy + dx * d.data[i + 1]);
		d.data[i + 2] = 
			Math.floor(cb * dy + dx * d.data[i + 2]);
		d.data[i + 3] = 255;
	}
	var d = ctx.putImageData(d, gx, gy);
}

